/*
 Ajanta - Java Linear Programming Library

 Copyright 2010 Raghu Kaippully.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */
package logic.simplex.model;

import java.util.*;

/**
 * Models a <a href="http://en.wikipedia.org/wiki/Linear_programming">linear
 * programming</a> problem. Ajanta requires that the linear programming problem
 * be represented in the standard form for solving it:
 *
 * <pre>
 *     Maximize:
 *         z = c<sub>1</sub>x<sub>1</sub> + c<sub>2</sub>x<sub>2</sub> + ... + c<sub>n</sub>x<sub>n</sub>
 *     Subject to:
 *         a<sub>i1</sub>x<sub>1</sub> + a<sub>i2</sub>x<sub>2</sub> + ... + a<sub>in</sub>x<sub>n</sub> (&le;, =, &ge;) b<sub>i</sub>,  i = 1, 2, ..., n
 *         x<sub>j</sub> &ge; 0,  j = 1, 2, ..., n
 *         b<sub>k</sub> &ge; 0,  k = 1, 2, ..., n
 * </pre>
 *
 * <p/>
 * After constructing an LPProblem, the objective function and constraints are
 * set on it using methods in this class. Later the problem is solved using an {@link LPSolver}.
 */
public class LPProblem {

    /**
     * The name of this LP problem, if any.
     */
    private String name;
    /**
     * The list of constraints used in this LP problem.
     */
    private List<LPConstraint> constraints = new ArrayList<LPConstraint>();
    /**
     * The objective function to be maximized.
     */
    private LPFunction objectiveFunction;

    /**
     * Constructs an LP problem.
     */
    public LPProblem() {
        this(null);
    }

    /**
     * Constructs an LP problem by specifying a name. The name will be printed
     * in the log messages if logging is enabled.
     *
     * @param name the name of this LP problem
     */
    public LPProblem(String name) {
        this.name = name;
    }

    /**
     * Set up the objective function to be maximized. This must be set before
     * solving the problem.
     *
     * @param function the objective function to be maximized
     * @return this LP problem
     */
    public LPProblem setObjectiveFunction(LPFunction function) {
        this.objectiveFunction = function;
        return this;
    }

    /**
     * Adds a constraint to this LP problem.
     *
     * @param constraint the constraint to be added
     * @return this LP problem
     */
    public LPProblem addConstraint(LPConstraint constraint) {
        constraints.add(constraint);
        return this;
    }

    /**
     * Gets the name of this LP problem.
     *
     * @return the name as set by {@link #LPProblem(String)}
     */
    public String getName() {
        return name;
    }

    /**
     * Returns a list of the constraints of this problem.
     *
     * @return the constraints of this problem
     */
    public List<LPConstraint> getConstraints() {
        return Collections.unmodifiableList(this.constraints);
    }

    /**
     * Gets the objective function of this LP problem.
     *
     * @return the objective function of this problem
     */
    public LPFunction getObjectiveFunction() {
        return this.objectiveFunction;
    }

    /**
     * Returns a collection of all variables used in the objective function and
     * constraints of this problem.
     *
     * @return a collection of all variables used in this problem
     */
    public Collection<LPVariable> getAllVariables() {
        Set<LPVariable> variables = new HashSet<LPVariable>();

        variables.addAll(getObjectiveFunction().getAllVariables());
        for (LPConstraint constraint : getConstraints()) {
            variables.addAll(constraint.getLHSExpression().getAllVariables());
        }

        return variables;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("Maximize:\n\t").append(objectiveFunction).append('\n');
        sb.append("Subject to:");
        for (LPConstraint c : constraints) {
            sb.append("\n\t").append(c);
        }
        return sb.toString();
    }
}
